#ifndef ABSTRACTRPCCHANNEL_HPP
#define ABSTRACTRPCCHANNEL_HPP

#include <kieper/PacketAssembler.hpp>

#include <boost/asio.hpp>
#include <boost/cstdint.hpp>
#include <boost/unordered_map.hpp>
#include <boost/utility.hpp>


#include <google/protobuf/descriptor.h>
#include <google/protobuf/message.h>
#include <google/protobuf/service.h>

namespace kieper {
    class AbstractRpcChannel : public ::google::protobuf::RpcChannel,
      private boost::noncopyable {
    public:
        AbstractRpcChannel();
        ~AbstractRpcChannel();

        void CallMethod(const google::protobuf::MethodDescriptor* method, 
          google::protobuf::RpcController* controller, 
          const google::protobuf::Message* request, 
          google::protobuf::Message* response,
          google::protobuf::Closure* done);
    protected:
        /**
         * Needs to be called by subclassers when the incoming message arrives
         */
        void onPacketReceived(unsigned char* message, size_t msgLen);
        boost::asio::io_service& getIoService();
    private:
        virtual void doSend(unsigned char *message, size_t msgLen) = 0;
        virtual unsigned char* getSendBuffer();
        virtual unsigned char* getRecvBuffer();
        
        /**
         * Message header size, in bytes
         */
        static const size_t HEADER_SIZE = 4;

        /**
         * Size of the default send buffer, in bytes
         */
        static const size_t SEND_BUFFER_SIZE = 6000;

        /**
         * Size of the default receive buffer, in bytes
         */
        static const size_t RECV_BUFFER_SIZE = 6000;
        
        /**
         * The io_service that may be used by implementing classes 
         */
        boost::asio::io_service ios_;

        /**
         * By default, this buffer is used to store the outgoing message.
         */
        unsigned char sendBuffer_[SEND_BUFFER_SIZE];
        
        /**
         * By default, this buffer is used to store the incoming message.
         */
        unsigned char recvBuffer_[RECV_BUFFER_SIZE];

        typedef boost::unordered_map<uint32_t, ::google::protobuf::Closure*> 
          Callbacks;
        
        /**
         * The map message id -> closure, where closure is the callback to be
         * executed when the response for the message of given id arrives.
         */
        Callbacks callbacks_;

        typedef boost::unordered_map<uint32_t, ::google::protobuf::Message*>
          Responses;
        Responses responses_;

        PacketAssembler packetAssembler_;
    };
}

#endif
